home *** CD-ROM | disk | FTP | other *** search
/ The Utilities Experience / The Utilities Experience - Volume 1.iso / software / emulation / frodo / src / display.c < prev    next >
C/C++ Source or Header  |  1996-02-03  |  14KB  |  646 lines

  1. /*
  2.  *  Display.c - Darstellung der C64-Grafik,
  3.  *              Handhabung des Emulatorfensters
  4.  *
  5.  *  Copyright (C) 1994-1996 by Christian Bauer
  6.  */
  7.  
  8. /*
  9.  *  Anmerkungen:
  10.  *  ------------
  11.  *
  12.  *  - Die Farbpalette besteht aus den 16 C64-Farben, 16mal wiederholt.
  13.  *    Dadurch spart man sich das Ausmaskieren der unteren 4 Bit bei den
  14.  *    VIC-Farbcodes. Allerdings muß dieses bei der Chunky->Planar-
  15.  *    Konvertierung für die Amiga-Chips erfolgen (der Algorithmus
  16.  *    setzt voraus, daß die oberen Nibbles Null sind).
  17.  */
  18.  
  19. #include <exec/types.h>
  20. #include <exec/libraries.h>
  21. #include <exec/memory.h>
  22. #include <intuition/intuition.h>
  23. #include <intuition/screens.h>
  24. #include <libraries/gadtools.h>
  25. #include <devices/timer.h>
  26. #include <clib/exec_protos.h>
  27. #include <clib/intuition_protos.h>
  28. #include <clib/graphics_protos.h>
  29. #include <clib/gadtools_protos.h>
  30. #include <clib/timer_protos.h>
  31. #include <string.h>
  32.  
  33. #include "Display.h"
  34. #include "SAM.h"
  35. #include "Prefs.h"
  36. #define CATCOMP_NUMBERS 1
  37. #include "LocStrings.h"
  38.  
  39.  
  40. // Aus Main.asm
  41. extern struct Library *GfxBase;
  42. extern struct Library *IntuitionBase;
  43. extern void ShowPrefs(void);
  44. extern void PutChProc(void);
  45. extern char *GetStr(int strnum);
  46.  
  47. // Aus 6510.asm
  48. extern struct Task *CPUTask;
  49. extern void Pause6510(void);
  50. extern void Resume6510(void);
  51. extern ULONG InvokeSAMSet;
  52.  
  53. // Aus 6526.asm
  54. extern void KeyPressed(char c);
  55.  
  56. // Aus 6569.asm
  57. extern APTR CURRENTA5;
  58. extern BYTE ChunkyBuf[];
  59. extern UWORD SkipLatch;
  60. extern UWORD LimitSpeed;
  61.  
  62. // Aus 6581.asm
  63. extern void PauseSound(void);
  64. extern void ResumeSound(void);
  65.  
  66. // Aus c2p4.asm
  67. extern void c2p4(UBYTE *fBUFFER, UBYTE *fBUFFER_CMP, APTR *planes, struct Task *task, ULONG signals);
  68. extern int Initc2p4(void);
  69. extern void Exitc2p4(void);
  70.  
  71.  
  72. // Prototypes
  73. void open_double_buf(int type, int width, int height);
  74. int handle_IDCMP(int done);
  75. int handle_menu(int menu, int item, int done);
  76.  
  77.  
  78. char has_gfx_39, has_gfx_40;  // Flags für Version der graphics.library
  79.  
  80. int current_type, current_width, current_height;
  81.  
  82. struct Screen *the_screen;
  83. struct Window *the_window;
  84. struct VisualInfo *the_visual_info;
  85. struct RastPort *the_rast_port;
  86. struct ViewPort *the_view_port;
  87. struct TextFont *topaz_font;
  88. struct Menu *the_menus;
  89.  
  90. // Für WritePixelArray8()
  91. struct BitMap *temp_bm;
  92. struct BitMap v37_temp_bm;
  93. struct RastPort temp_rp;
  94.  
  95. // Double Buffering
  96. struct ScreenBuffer *scr_buf[2];
  97. int inv_buf_num;
  98. char using_db;                // Flag: Double Buffering wird benutzt
  99.  
  100. // c2p4
  101. APTR comparison_buf[2];
  102. char c2p4_signal;            // Signal: c2p4 fertig
  103. LONGBITS c2p4_set;
  104. char must_wait_for_c2p4;    // Flag: Auf c2p4 muß gewartet werden
  105.  
  106. // Geschwindigkeitsanzeige
  107. struct MsgPort *timer_port;
  108. struct timerequest *timer_io;
  109.  
  110.  
  111. const struct TextAttr topaz_attr = {
  112.   "topaz.font", 8, FS_NORMAL, 0
  113. };
  114.  
  115. struct NewMenu new_menus[] = {
  116.   NM_TITLE, MSG_EMULATION_MENU, NULL, 0, 0, NULL,
  117.   NM_ITEM, MSG_SETTINGS_MENU, NULL, 0, 0, NULL,
  118.   NM_ITEM, MSG_SAM_MENU, "M", 0, 0, NULL,
  119.   NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL,
  120.   NM_ITEM, MSG_QUIT_MENU, "Q", 0, 0, NULL,
  121.   NM_END, NULL, NULL, 0, 0, NULL
  122. };
  123.  
  124. const UBYTE palette_red[16] = {
  125.   0, 15, 12, 0, 15, 0, 0, 15, 15, 8, 15, 4, 8, 8, 8, 12
  126. };
  127.  
  128. const UBYTE palette_green[16] = {
  129.   0, 15, 0, 15, 0, 12, 0, 15, 8, 4, 8, 4, 8, 15, 8, 12
  130. };
  131.  
  132. const UBYTE palette_blue[16] = {
  133.   0, 15, 0, 12, 15, 0, 12, 0, 0, 0, 8, 4, 8, 8, 15, 12
  134. };
  135.  
  136.  
  137. /*
  138.  *  Screen und Fenster öffnen, alles vorbereiten
  139.  *  0: OK, 1: Fehler beim Screen-Öffnen, 2: Kein Speicher
  140.  */
  141.  
  142. int OpenDisplay(int type, ULONG display_id, UWORD overscan, int width, int height)
  143. {
  144.   int depth;
  145.   int i;
  146.  
  147.   has_gfx_39 = (GfxBase->lib_Version >= 39);
  148.   has_gfx_40 = (GfxBase->lib_Version >= 40);
  149.  
  150.   must_wait_for_c2p4 = FALSE;
  151.   using_db = FALSE;
  152.  
  153.   current_type = type;
  154.   current_width = width;
  155.   current_height = height;
  156.  
  157.   switch (type) {
  158.     case SCRTYPE_8BIT: depth = 8; break;
  159.     case SCRTYPE_4BIT: depth = 4; break;
  160.     case SCRTYPE_1BIT: depth = 1; break;
  161.   }
  162.  
  163.   if (!(the_screen = OpenScreenTags(NULL,
  164.     SA_Width, width,
  165.     SA_Height, height,
  166.     SA_DisplayID, display_id,
  167.     SA_Overscan, overscan,
  168.     SA_Depth, depth,
  169.     SA_Quiet, TRUE,
  170.     SA_AutoScroll, TRUE,
  171.     TAG_DONE)))
  172.     return 1;
  173.  
  174.   the_rast_port = &the_screen->RastPort;
  175.   the_view_port = &the_screen->ViewPort;
  176.  
  177.   if (topaz_font = OpenFont(&topaz_attr))
  178.     SetFont(the_rast_port, topaz_font);
  179.   SetAPen(the_rast_port, 1);
  180.  
  181.   if (!(the_visual_info = GetVisualInfo(the_screen, NULL)))
  182.     return 2;
  183.  
  184.   if (!(the_menus = CreateMenus(new_menus, GTMN_FullMenu, TRUE, TAG_DONE)))
  185.     return 2;
  186.   LayoutMenus(the_menus, the_visual_info, GTMN_NewLookMenus, TRUE, TAG_DONE);
  187.  
  188.   if (!(the_window = OpenWindowTags(NULL,
  189.     WA_Left, 0,
  190.     WA_Top, 0,
  191.     WA_Width, width,
  192.     WA_Height, height,
  193.     WA_CustomScreen, the_screen,
  194.     WA_IDCMP, IDCMP_RAWKEY | IDCMP_MENUPICK | IDCMP_MENUVERIFY,
  195.     WA_Backdrop, TRUE,
  196.     WA_Borderless, TRUE,
  197.     WA_NoCareRefresh, TRUE,
  198.     WA_Activate, TRUE,
  199.     WA_NewLookMenus, TRUE,
  200.     TAG_DONE)))
  201.     return 2;
  202.  
  203.   SetMenuStrip(the_window, the_menus);
  204.  
  205.   switch (type) {
  206.     case SCRTYPE_8BIT:
  207.  
  208.       // Farbpalette laden
  209.       for (i=0; i<256; i++)
  210.         SetRGB4(the_view_port, i,
  211.           palette_red[i & 0xf], palette_green[i & 0xf], palette_blue[i & 0xf]);
  212.  
  213.       // Temporären RastPort für WritePixelArray8() anlegen
  214.       if (has_gfx_39) {
  215.         if (!(temp_bm = AllocBitMap(width, 1, 8, 0, NULL)))
  216.           return 2;
  217.       } else {
  218.         InitBitMap(temp_bm = &v37_temp_bm, depth, width, 1);
  219.         for (i=0; i<8; i++)
  220.           if (!(v37_temp_bm.Planes[i] = AllocRaster(width, 1)))
  221.             return 2;
  222.       }
  223.  
  224.       InitRastPort(&temp_rp);
  225.       temp_rp.BitMap = temp_bm;
  226.  
  227.       CURRENTA5 = ChunkyBuf;  // Wird nicht mehr verändert
  228.       break;
  229.  
  230.     case SCRTYPE_4BIT:
  231.  
  232.       if (!Initc2p4())
  233.         return 2;
  234.  
  235.       // Speicher für Comparison Buffer holen
  236.       if (!(comparison_buf[0] = AllocVec(width * height, MEMF_PUBLIC | MEMF_CLEAR)))
  237.         return 2;
  238.  
  239.       // Farbpalette laden
  240.       for (i=0; i<16; i++)
  241.         SetRGB4(the_view_port, i,
  242.           palette_red[i & 0xf], palette_green[i & 0xf], palette_blue[i & 0xf]);
  243.  
  244.       CURRENTA5 = ChunkyBuf;
  245.  
  246.       open_double_buf(type, width, height);
  247.       must_wait_for_c2p4 = FALSE;
  248.       break;
  249.  
  250.     case SCRTYPE_1BIT:
  251.  
  252.       SetRGB4(the_view_port, 0, 0, 0, 0);        // schwarz
  253.       SetRGB4(the_view_port, 1, 15, 15, 15);    // weiß
  254.  
  255.       CURRENTA5 = the_rast_port->BitMap->Planes[0];
  256.  
  257.       open_double_buf(type, width, height);
  258.       break;
  259.   }
  260.  
  261.   return 0;
  262. }
  263.  
  264.  
  265. /*
  266.  *  Double Buffering einrichten
  267.  */
  268.  
  269. void open_double_buf(int type, int width, int height)
  270. {
  271.   using_db = FALSE;
  272.   if (IntuitionBase->lib_Version >= 39) {
  273.  
  274.     if (!(scr_buf[0] = AllocScreenBuffer(the_screen, NULL, SB_SCREEN_BITMAP)))
  275.       return;
  276.  
  277.     if (!(scr_buf[1] = AllocScreenBuffer(the_screen, NULL, SB_COPY_BITMAP)))
  278.       return;
  279.  
  280.     WaitTOF(); WaitTOF();
  281.     inv_buf_num = 1;
  282.  
  283.     if (type == SCRTYPE_4BIT) {
  284.       CURRENTA5 = ChunkyBuf;
  285.       if (!(comparison_buf[1] = AllocVec(width * height, MEMF_PUBLIC | MEMF_CLEAR)))
  286.         return;
  287.     } else {
  288.       CURRENTA5 = scr_buf[1]->sb_BitMap->Planes[0];
  289.     }
  290.  
  291.     using_db = TRUE;
  292.   }
  293. }
  294.  
  295.  
  296. /*
  297.  *  Screen und Fenster schließen
  298.  */
  299.  
  300. void CloseDisplay(void)
  301. {
  302.   int i;
  303.  
  304.   if (current_type == SCRTYPE_4BIT)
  305.     Exitc2p4();
  306.  
  307.   if (comparison_buf[0]) {
  308.     FreeVec(comparison_buf[0]);
  309.     comparison_buf[0] = NULL;
  310.   }
  311.  
  312.   if (comparison_buf[1]) {
  313.     FreeVec(comparison_buf[1]);
  314.     comparison_buf[1] = NULL;
  315.   }
  316.  
  317.   FreeScreenBuffer(the_screen, scr_buf[0]);  // NULL ist OK
  318.   scr_buf[0] = NULL;
  319.   FreeScreenBuffer(the_screen, scr_buf[1]);
  320.   scr_buf[1] = NULL;
  321.  
  322.   if (the_menus) {
  323.     ClearMenuStrip(the_window);
  324.     FreeMenus(the_menus);
  325.     the_menus = NULL;
  326.   }
  327.  
  328.   if (the_window) {
  329.     CloseWindow(the_window);
  330.     the_window = NULL;
  331.   }
  332.  
  333.   // Temporären RastPort schließen
  334.   if (temp_bm) {
  335.     if (has_gfx_39)
  336.       FreeBitMap(temp_bm);
  337.     else {
  338.       for (i=0; i<8; i++) {
  339.         FreeRaster(v37_temp_bm.Planes[i], current_width, current_height);
  340.         v37_temp_bm.Planes[i] = NULL;
  341.       }
  342.     }
  343.     temp_bm = NULL;
  344.   }
  345.  
  346.   if (the_visual_info) {
  347.     FreeVisualInfo(the_visual_info);
  348.     the_visual_info = NULL;
  349.   }
  350.  
  351.   if (topaz_font) {
  352.     CloseFont(topaz_font);
  353.     topaz_font = NULL;
  354.   }
  355.  
  356.   if (the_screen) {
  357.     CloseScreen(the_screen);
  358.     the_screen = NULL;
  359.   }
  360. }
  361.  
  362.  
  363. /*
  364.  *  Vom 6510-Task aus nötige Initialisierungen
  365.  *  Wird vom 6510-Task aufgerufen
  366.  */
  367.  
  368. void InitDisplayFrom6510(void)
  369. {
  370.   // Signal für c2p4
  371.   c2p4_signal = AllocSignal(-1);
  372.   c2p4_set = 1 << c2p4_signal;
  373.  
  374.   // TimerIO einrichten
  375.   if (timer_port = CreateMsgPort()) {
  376.     if (timer_io = CreateIORequest(timer_port, sizeof(struct timerequest))) {
  377.       OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *)timer_io, 0);
  378.  
  379.       // timer_io für Speed Limiter starten
  380.       timer_io->tr_node.io_Command = TR_ADDREQUEST;
  381.       timer_io->tr_time.tv_secs = 0;
  382.       timer_io->tr_time.tv_micro = SkipLatch * 20000;  // 20ms pro Bild
  383.       SendIO((struct IORequest *)timer_io);
  384.     }
  385.   }
  386. }
  387.  
  388.  
  389. /*
  390.  *  Vom 6510-Task aus nötige Aufräumarbeiten
  391.  *  Wird vom 6510-Task aufgerufen
  392.  */
  393.  
  394. void ExitDisplayFrom6510(void)
  395. {
  396.   if (timer_io) {
  397.     if (!CheckIO((struct IORequest *)timer_io))
  398.       WaitIO((struct IORequest *)timer_io);
  399.     CloseDevice((struct IORequest *)timer_io);
  400.     DeleteIORequest((struct IORequest *)timer_io);
  401.     timer_io = NULL;
  402.   }
  403.  
  404.   if (timer_port) {
  405.     DeleteMsgPort(timer_port);
  406.     timer_port = NULL;
  407.   }
  408.  
  409.   // Ggf. auf Signal von c2p4 warten
  410.   if (must_wait_for_c2p4)
  411.     Wait(c2p4_set);
  412.  
  413.   FreeSignal(c2p4_signal);
  414. }
  415.  
  416.  
  417. /*
  418.  *  C64-Grafik neu darstellen (VBlank, Double Buffering)
  419.  *  Wird vom 6510-Task aufgerufen
  420.  */
  421.  
  422. struct timeval start_time, end_time;
  423. char speed_str[20];
  424. int percent;
  425.  
  426. void RedrawDisplay(void)
  427. {
  428.   switch (current_type) {
  429.     // SCRTYPE_8BIT: WritePixelLine in 6569.asm
  430.  
  431.     case SCRTYPE_4BIT:        // Double Buffering und c2p-Konvertierung
  432.       if (must_wait_for_c2p4)
  433.         Wait(c2p4_set);
  434.  
  435.       if (using_db) {
  436.         while (!ChangeScreenBuffer(the_screen, scr_buf[inv_buf_num]))
  437.           WaitTOF();
  438.         inv_buf_num ^= 1;
  439.         c2p4(ChunkyBuf, comparison_buf[inv_buf_num], scr_buf[inv_buf_num]->sb_BitMap->Planes, CPUTask, c2p4_set);
  440.       } else
  441.         c2p4(ChunkyBuf, comparison_buf[0], the_rast_port->BitMap->Planes, CPUTask, c2p4_set);
  442.  
  443.       must_wait_for_c2p4 = TRUE;
  444.       CURRENTA5 = ChunkyBuf;
  445.       break;
  446.  
  447.     case SCRTYPE_1BIT:        // Double Buffering
  448.       if (using_db) {
  449.         while (!ChangeScreenBuffer(the_screen, scr_buf[inv_buf_num]))
  450.           WaitTOF();
  451.         inv_buf_num ^= 1;
  452.         CURRENTA5 = scr_buf[inv_buf_num]->sb_BitMap->Planes[0];
  453.       } else
  454.         CURRENTA5 = the_rast_port->BitMap->Planes[0];
  455.       break;
  456.   }
  457.  
  458.   if (timer_io) {
  459.     // timer_io abbrechen, wenn Speed Limiter aus ist
  460.     if (!LimitSpeed)
  461.       if (!CheckIO((struct IORequest *)timer_io))
  462.         AbortIO((struct IORequest *)timer_io);
  463.  
  464.     WaitIO((struct IORequest *)timer_io);
  465.  
  466.     // timer_io neu starten
  467.     timer_io->tr_node.io_Command = TR_ADDREQUEST;
  468.     timer_io->tr_time.tv_secs = 0;
  469.     timer_io->tr_time.tv_micro = SkipLatch * 20000;  // 20ms pro Bild
  470.     SendIO((struct IORequest *)timer_io);
  471.   }
  472.  
  473.   // Zeit seit letztem Aufruf ermitteln
  474.   GetSysTime(&end_time);
  475.   SubTime(&end_time, &start_time);
  476.   GetSysTime(&start_time);
  477.  
  478.   // Ein Bild sollte 20ms dauern
  479.   percent = (20000 * 100 / end_time.tv_micro + 1) * SkipLatch;
  480.  
  481.   RawDoFmt("%ld%%", &percent, &PutChProc, speed_str);
  482.   Move(the_rast_port, 4, current_height-6);
  483.   Text(the_rast_port, speed_str, strlen(speed_str));
  484. }
  485.  
  486.  
  487. /*
  488.  *  C64-Grafik nach hinten
  489.  */
  490.  
  491. void EmulToBack(void)
  492. {
  493.   ScreenToBack(the_screen);
  494.   ModifyIDCMP(the_window, IDCMP_RAWKEY);
  495.   ClearMenuStrip(the_window);
  496. }
  497.  
  498.  
  499. /*
  500.  *  C64-Grafik nach vorne
  501.  */
  502.  
  503. void EmulToFront(void)
  504. {
  505.   ScreenToFront(the_screen);
  506.   ModifyIDCMP(the_window, IDCMP_RAWKEY | IDCMP_MENUPICK | IDCMP_MENUVERIFY);
  507.   SetMenuStrip(the_window, the_menus);
  508. }
  509.  
  510.  
  511. /*
  512.  *  Event-Schleife des Fensters
  513.  */
  514.  
  515. void EventLoop(void)
  516. {
  517.   int done = FALSE;
  518.   ULONG signals;
  519.  
  520.   while (!done) {
  521.     signals = Wait((1 << the_window->UserPort->mp_SigBit) | InvokeSAMSet);
  522.  
  523.     if (signals & (1 << the_window->UserPort->mp_SigBit))
  524.       done = handle_IDCMP(done);
  525.  
  526.     if (signals & InvokeSAMSet)
  527.       handle_menu(0, 1, done);
  528.   }
  529. }
  530.  
  531.  
  532. /*
  533.  *  IDCMP-Messages handhaben
  534.  */
  535.  
  536. int handle_IDCMP(int done)
  537. {
  538.   struct IntuiMessage *msg;
  539.   ULONG class;
  540.   UWORD code;
  541.   UWORD menu_number;
  542.   struct MenuItem *item;
  543.  
  544.   while (!done && (msg = GetMsg(the_window->UserPort))) {
  545.     class = msg->Class;
  546.     code = msg->Code;
  547.  
  548.     if (class == IDCMP_MENUVERIFY)
  549.       Pause6510();    // Subtask stoppen
  550.  
  551.     ReplyMsg((struct Message *)msg);
  552.  
  553.     switch (class) {
  554.       case IDCMP_MENUPICK:
  555.         Resume6510();  // Subtask fortführen
  556.  
  557.         menu_number = code;
  558.         while (!done && (menu_number != MENUNULL)) {
  559.           item = ItemAddress(the_window->MenuStrip, menu_number);
  560.           done = handle_menu(MENUNUM(menu_number), ITEMNUM(menu_number), done);
  561.           menu_number = item->NextSelect;
  562.         }
  563.         break;
  564.  
  565.       case IDCMP_RAWKEY:
  566.         if (code == 0x58)    // F9 -> Einstellungen
  567.           handle_menu(0, 0, done);
  568.         else
  569.           KeyPressed(code);
  570.         break;
  571.     }
  572.   }
  573.  
  574.   return done;
  575. }
  576.  
  577.  
  578. /*
  579.  *  Menü wurde ausgewählt
  580.  */
  581.  
  582. int handle_menu(int menu, int item, int done)
  583. {
  584.   if (menu == 0) {
  585.     switch (item) {
  586.       case 0:        // Einstellungen
  587.         Pause6510();
  588.         PauseSound();
  589.         EmulToBack();
  590.         ShowPrefs();
  591.         EmulToFront();
  592.         ResumeSound();
  593.         Resume6510();
  594.         break;
  595.  
  596.       case 1:        // SAM
  597.         Pause6510();
  598.         PauseSound();
  599.         EmulToBack();
  600.         SAM();
  601.         EmulToFront();
  602.         ResumeSound();
  603.         Resume6510();
  604.         break;
  605.  
  606.       case 3:        // Quit
  607.         done = TRUE;
  608.         break;
  609.     }
  610.   }
  611.  
  612.   return done;
  613. }
  614.  
  615.  
  616. /*
  617.  *  Requester anzeigen
  618.  */
  619.  
  620. int ShowRequester(int text, int gads, APTR args)
  621. {
  622.   struct EasyStruct es;
  623.  
  624.   es.es_StructSize = sizeof(struct EasyStruct);
  625.   es.es_Flags = 0;
  626.   es.es_Title = GetStr(MSG_REQTITLE);
  627.   es.es_TextFormat = GetStr(text);
  628.   es.es_GadgetFormat = GetStr(gads);
  629.  
  630.   return EasyRequestArgs(NULL, &es, NULL, args);
  631. }
  632.  
  633.  
  634. /*
  635.  *  Lokalisierungen vornehmen
  636.  */
  637.  
  638. void LocalizeDisplay(void)
  639. {
  640.   int i;
  641.  
  642.   for (i=0; new_menus[i].nm_Type != NM_END; i++)
  643.     if (new_menus[i].nm_Label != NM_BARLABEL)
  644.       new_menus[i].nm_Label = GetStr((int)new_menus[i].nm_Label);
  645. }
  646.